<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="qLF4X" id="qLF4X"><span data-lake-id="u17188b7a" id="u17188b7a">典型回答</span></h1>
  <p data-lake-id="uc7b6fe94" id="uc7b6fe94"><br></p>
  <p data-lake-id="u4cbccf90" id="u4cbccf90"><span data-lake-id="ub731b47b" id="ub731b47b">单例模式主要是通过把一个类的构造方法私有化，来避免重复创建多个对象的。那么，想要破坏单例，只要想办法能够执行到这个私有的构造方法就行了。</span></p>
  <p data-lake-id="u8de2dfb9" id="u8de2dfb9"><br></p>
  <p data-lake-id="u39ae0f81" id="u39ae0f81"><span data-lake-id="u260bbfe3" id="u260bbfe3">一般来说做法有使用反射及使用反序列化都可以破坏单例。</span></p>
  <p data-lake-id="uca4d9009" id="uca4d9009"><span data-lake-id="u2effce6e" id="u2effce6e">​</span><br></p>
  <h1 data-lake-id="Y3tRD" id="Y3tRD"><span data-lake-id="uf0f91042" id="uf0f91042">扩展知识</span></h1>
  <p data-lake-id="u00f8314b" id="u00f8314b"><span data-lake-id="ub71c2d9e" id="ub71c2d9e">我们先通过双重校验锁的方式创建一个单例，后文会通过反射及反序列化的方式尝试破坏这个单例。</span></p>
  <p data-lake-id="ub0560d13" id="ub0560d13"><span data-lake-id="ue1303322" id="ue1303322">​</span><br></p>
  <pre lang="java"><code>
package com.hollis;
import java.io.Serializable;
/**
 * Created by hollis on 16/2/5.
 * 使用双重校验锁方式实现单例
 */
public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
</code></pre>
  <p data-lake-id="u5edeb33a" id="u5edeb33a"><br></p>
  <p data-lake-id="u443f3fd4" id="u443f3fd4"><br></p>
  <h2 data-lake-id="N74uI" id="N74uI"><span data-lake-id="ufe751916" id="ufe751916">反射破坏单例</span></h2>
  <p data-lake-id="u7ddc3ac7" id="u7ddc3ac7"><br></p>
  <p data-lake-id="u3e26ebd2" id="u3e26ebd2"><span data-lake-id="ued998e4b" id="ued998e4b">我们尝试通过反射技术，来破坏单例：</span></p>
  <p data-lake-id="u19e53bc7" id="u19e53bc7"><br></p>
  <pre lang="java"><code>
Singleton singleton1 = Singleton.getSingleton();

//通过反射获取到构造函数
Constructor&lt;Singleton&gt; constructor = Singleton.class.getDeclaredConstructor();
//将构造函数设置为可访问类型
constructor.setAccessible(true);
//调用构造函数的newInstance创建一个对象
Singleton singleton2 = constructor.newInstance();
//判断反射创建的对象和之前的对象是不是同一个对象
System.out.println(s1 == s2);
</code></pre>
  <p data-lake-id="u80b7afb1" id="u80b7afb1"><br></p>
  <p data-lake-id="uf0a291d0" id="uf0a291d0"><span data-lake-id="u6a7bbf24" id="u6a7bbf24">以上代码，输出结果为false，也就是说通过反射技术，我们给单例对象创建出来了一个"兄弟"。</span></p>
  <p data-lake-id="ue537acfe" id="ue537acfe"><br></p>
  <blockquote data-lake-id="ua66d8c15" id="ua66d8c15">
   <p data-lake-id="ub8c25dec" id="ub8c25dec"><span data-lake-id="u1a78e16d" id="u1a78e16d">setAccessible(true)，使得反射对象在使用时应该取消 Java 语言访问检查，使得私有的构造函数能够被访问。</span></p>
  </blockquote>
  <p data-lake-id="u396735de" id="u396735de"><br></p>
  <h2 data-lake-id="WKH7c" id="WKH7c"><span data-lake-id="u0f59ec3a" id="u0f59ec3a">反序列化破坏单例</span></h2>
  <p data-lake-id="u9c1b70de" id="u9c1b70de"><br></p>
  <p data-lake-id="u65565d94" id="u65565d94"><span data-lake-id="ue18f7e23" id="ue18f7e23">我们尝试通过序列化+反序列化来破坏一下单例：</span></p>
  <p data-lake-id="u5bd2ce2f" id="u5bd2ce2f"><br></p>
  <pre lang="java"><code>
package com.hollis;
import java.io.*;
/**
 * Created by hollis on 16/2/5.
 */
public class SerializableDemo1 {
    //为了便于理解，忽略关闭流操作及删除文件操作。真正编码时千万不要忘记
    //Exception直接抛出
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        //判断是否是同一个对象
        System.out.println(newInstance == Singleton.getSingleton());
    }
}
//false
</code></pre>
  <p data-lake-id="u5f37c14f" id="u5f37c14f"><br></p>
  <p data-lake-id="ua3af3b22" id="ua3af3b22"><span data-lake-id="uf6769a58" id="uf6769a58">输出结构为false，说明：</span></p>
  <p data-lake-id="u293965d1" id="u293965d1"><br></p>
  <blockquote data-lake-id="u351539e1" id="u351539e1">
   <p data-lake-id="u16f8dd94" id="u16f8dd94"><span data-lake-id="ubbaebec2" id="ubbaebec2">通过对Singleton的序列化与反序列化得到的对象是一个新的对象，这就破坏了Singleton的单例性。</span></p>
  </blockquote>
  <p data-lake-id="u9df589c7" id="u9df589c7"><br></p>
  <p data-lake-id="ub3576339" id="ub3576339"><span data-lake-id="u4b6033f8" id="u4b6033f8">这里，在介绍如何解决这个问题之前，我们先来深入分析一下，为什么会这样？在反序列化的过程中到底发生了什么。</span></p>
  <p data-lake-id="u8eba8115" id="u8eba8115"><br></p>
  <h3 data-lake-id="any7J" id="any7J"><span data-lake-id="u036e5108" id="u036e5108">ObjectInputStream</span></h3>
  <p data-lake-id="u43bb16f8" id="u43bb16f8"><br></p>
  <p data-lake-id="ufe90c49b" id="ufe90c49b"><span data-lake-id="u70be6baf" id="u70be6baf">对象的序列化过程通过ObjectOutputStream和ObjectInputStream来实现的，那么带着刚刚的问题，分析一下ObjectInputStream 的</span><code data-lake-id="u7a4c8ce0" id="u7a4c8ce0"><span data-lake-id="ue52469df" id="ue52469df">readObject</span></code><span data-lake-id="u2c101ffa" id="u2c101ffa"> 方法执行情况到底是怎样的。</span></p>
  <p data-lake-id="u09d99dfa" id="u09d99dfa"><br></p>
  <p data-lake-id="u1dc407f4" id="u1dc407f4"><span data-lake-id="uf1663eb9" id="uf1663eb9">为了节省篇幅，这里给出ObjectInputStream的</span><code data-lake-id="ubbed51e9" id="ubbed51e9"><span data-lake-id="u57da7f98" id="u57da7f98">readObject</span></code><span data-lake-id="u86c9d6c2" id="u86c9d6c2">的调用栈：</span></p>
  <p data-lake-id="u6cb352e9" id="u6cb352e9"><br></p>
  <p data-lake-id="u0771b5c4" id="u0771b5c4"><img src="https://www.hollischuang.com/wp-content/uploads/2016/02/640.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u80b51c41" id="u80b51c41"><br></p>
  <p data-lake-id="ud415aa83" id="ud415aa83"><span data-lake-id="u5a015247" id="u5a015247">这里看一下重点代码，</span><code data-lake-id="ua3cc40fa" id="ua3cc40fa"><span data-lake-id="uede23169" id="uede23169">readOrdinaryObject</span></code><span data-lake-id="u18a0e4ef" id="u18a0e4ef">方法的代码片段： code 3</span></p>
  <p data-lake-id="u5fc2bb27" id="u5fc2bb27"><br></p>
  <pre lang="java"><code>
private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        //此处省略部分代码

        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null;
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }

        //此处省略部分代码

        if (obj != null &amp;&amp;
            handles.lookupException(passHandle) == null &amp;&amp;
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj);
            if (unshared &amp;&amp; rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }

        return obj;
    }
</code></pre>
  <p data-lake-id="ua016acee" id="ua016acee"><br></p>
  <p data-lake-id="u6bf8acc2" id="u6bf8acc2"><span data-lake-id="u6f4266c8" id="u6f4266c8">code 3 中主要贴出两部分代码。先分析第一部分：</span></p>
  <p data-lake-id="u27e485e5" id="u27e485e5"><br></p>
  <p data-lake-id="ub2bcbb42" id="ub2bcbb42"><span data-lake-id="uce9183db" id="uce9183db">code 3.1</span></p>
  <p data-lake-id="u3a9b9da7" id="u3a9b9da7"><br></p>
  <pre lang="java"><code>
Object obj;
try {
    obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
    throw (IOException) new InvalidClassException(desc.forClass().getName(),"unable to create instance").initCause(ex);
}
</code></pre>
  <p data-lake-id="uc2ec16b3" id="uc2ec16b3"><br></p>
  <p data-lake-id="u596f0261" id="u596f0261"><span data-lake-id="ub02d5815" id="ub02d5815">这里创建的这个obj对象，就是本方法要返回的对象，也可以暂时理解为是ObjectInputStream的</span><code data-lake-id="uab10d7fd" id="uab10d7fd"><span data-lake-id="u5b90b1cc" id="u5b90b1cc">readObject</span></code><span data-lake-id="u1c4436f1" id="u1c4436f1">返回的对象。</span></p>
  <p data-lake-id="u2d238b4c" id="u2d238b4c"><br></p>
  <p data-lake-id="u56c583c1" id="u56c583c1"><img src="https://www.hollischuang.com/wp-content/uploads/2016/02/641.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uea137ea0" id="uea137ea0"><br></p>
  <blockquote data-lake-id="uef48d599" id="uef48d599">
   <p data-lake-id="uefbd14d6" id="uefbd14d6"><code data-lake-id="u0c478658" id="u0c478658"><span data-lake-id="u66d11715" id="u66d11715">isInstantiable</span></code><span data-lake-id="u605898aa" id="u605898aa">：如果一个serializable/externalizable的类可以在运行时被实例化，那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。</span></p>
   <p data-lake-id="uf770be39" id="uf770be39"><span data-lake-id="ua8376f68" id="ua8376f68"> </span></p>
   <p data-lake-id="u95c20130" id="u95c20130"><code data-lake-id="u6bff25e7" id="u6bff25e7"><span data-lake-id="ud66efa40" id="ud66efa40">desc.newInstance</span></code><span data-lake-id="u3fa448ca" id="u3fa448ca">：该方法通过反射的方式新建一个对象。</span></p>
  </blockquote>
  <p data-lake-id="u1ecf8d9d" id="u1ecf8d9d"><br></p>
  <p data-lake-id="u4ebc44a8" id="u4ebc44a8"><span data-lake-id="u638a5880" id="u638a5880">然后看一下</span><code data-lake-id="ua6c14a07" id="ua6c14a07"><span data-lake-id="uc8ba4dcc" id="uc8ba4dcc">newInstance</span></code><span data-lake-id="ucb6b9dc9" id="ucb6b9dc9">的源码：</span></p>
  <p data-lake-id="u6e6c5aff" id="u6e6c5aff"><span data-lake-id="u449d72be" id="u449d72be">​</span><br></p>
  <pre lang="java"><code>
public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class&lt;?&gt; caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, null, modifiers);
        }
    }
    if ((clazz.getModifiers() &amp; Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
    ConstructorAccessor ca = constructorAccessor;   // read volatile
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;
}
</code></pre>
  <p data-lake-id="u8e2778cc" id="u8e2778cc"><br></p>
  <p data-lake-id="u260a5ebc" id="u260a5ebc"><span data-lake-id="ua7399a99" id="ua7399a99">其中关键的就是</span><code data-lake-id="u695f6743" id="u695f6743"><span data-lake-id="uc92f759d" id="uc92f759d">T inst = (T) ca.newInstance(initargs);</span></code><span data-lake-id="u7f1d905a" id="u7f1d905a">这一步，这里实现的话在BootstrapConstructorAccessorImpl中，实现如下：</span></p>
  <p data-lake-id="u74d53df5" id="u74d53df5"><span data-lake-id="u08d4e436" id="u08d4e436">​</span><br></p>
  <pre lang="java"><code>
public Object newInstance(Object[] args)
    throws IllegalArgumentException, InvocationTargetException
{
    try {
        return UnsafeFieldAccessorImpl.unsafe.
            allocateInstance(constructor.getDeclaringClass());
    } catch (InstantiationException e) {
        throw new InvocationTargetException(e);
    }
}
</code></pre>
  <p data-lake-id="u6294c9bd" id="u6294c9bd"><span data-lake-id="u349fe19e" id="u349fe19e">​</span><br></p>
  <p data-lake-id="u0780f098" id="u0780f098"><span data-lake-id="u01100cca" id="u01100cca">可以看到，</span><strong><span data-lake-id="u922b150d" id="u922b150d">这里通过Java 的 Unsafe 机制来创建对象的，而不是通过调用构造函数。</span></strong><span data-lake-id="ucbee547e" id="ucbee547e">这意味着即使类的构造函数是私有的，反序列化仍然可以创建该类的实例，因为它不依赖于常规的构造过程。</span></p>
  <p data-lake-id="u6cdda048" id="u6cdda048"><br></p>
  <p data-lake-id="ub012d61f" id="ub012d61f"><span data-lake-id="uefec7dfb" id="uefec7dfb">所以。到目前为止，也就可以解释，为什么序列化可以破坏单例了？</span></p>
  <p data-lake-id="uaa2689c9" id="uaa2689c9"><br></p>
  <blockquote data-lake-id="u9e1a1c60" id="u9e1a1c60">
   <p data-lake-id="u2a4af369" id="u2a4af369"><span data-lake-id="ub144c295" id="ub144c295">答：序列化会通过Unsafe直接分配内存的方式来创建一个新的对象。</span></p>
  </blockquote>
  <p data-lake-id="u90c03dad" id="u90c03dad"><br></p>
  <h2 data-lake-id="25f9c7fa" id="25f9c7fa"><span data-lake-id="u932cb0e7" id="u932cb0e7">总结</span></h2>
  <p data-lake-id="u21b0bf4f" id="u21b0bf4f"><br></p>
  <p data-lake-id="u82c154bb" id="u82c154bb"><span data-lake-id="ub7face04" id="ub7face04">在涉及到序列化的场景时，要格外注意他对单例的破坏。</span></p>
  <p data-lake-id="uc98c10eb" id="uc98c10eb"><br></p>
  <h2 data-lake-id="OCQ7b" id="OCQ7b"><span data-lake-id="u45d77679" id="u45d77679">如何避免单例被破坏</span></h2>
  <p data-lake-id="ua8193a26" id="ua8193a26"><br></p>
  <h3 data-lake-id="NMASz" id="NMASz"><span data-lake-id="u27fae12b" id="u27fae12b">避免反射破坏单例</span></h3>
  <p data-lake-id="uc8d16aa3" id="uc8d16aa3"><br></p>
  <p data-lake-id="u1e132239" id="u1e132239"><span data-lake-id="u9b6fba73" id="u9b6fba73">反射是调用默认的构造函数创建出来的，只需要我们改造下构造函数，使其在反射调用的时候识别出来对象是不是被创建过就行了：</span></p>
  <p data-lake-id="u2d3c6933" id="u2d3c6933"><br></p>
  <pre lang="java"><code>
private Singleton() {
    if (singleton != null){        
    throw new RuntimeException("单例对象只能创建一次... ");
}
</code></pre>
  <h3 data-lake-id="maB7m" id="maB7m"><br></h3>
  <h3 data-lake-id="yCWXi" id="yCWXi"><span data-lake-id="u23094e9a" id="u23094e9a">避免反序列化破坏单例</span></h3>
  <p data-lake-id="u2f63c05a" id="u2f63c05a"><br></p>
  <p data-lake-id="u4757d17d" id="u4757d17d"><span data-lake-id="u1b2d5ef8" id="u1b2d5ef8">解决反序列化的破坏单例，只需要我们自定义反序列化的策略就行了，就是说我们不要让他走默认逻辑一直调用到Unsafe创建对象，而是我们干预他的这个过程，干预方式就是在Singleton类中定义</span><code data-lake-id="u0f9d9e58" id="u0f9d9e58"><span data-lake-id="u47eba489" id="u47eba489">readResolve</span></code><span data-lake-id="uce394480" id="uce394480">，这样就可以解决该问题：</span></p>
  <p data-lake-id="ua0464375" id="ua0464375"><br></p>
  <pre lang="java"><code>
package com.hollis;
import java.io.Serializable;
/**
 * Created by hollis on 16/2/5.
 * 使用双重校验锁方式实现单例
 */
public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    private Object readResolve() {
        return singleton;
    }
}
</code></pre>
  <p data-lake-id="u1fa31cf3" id="u1fa31cf3"><br></p>
  <p data-lake-id="ud3e38d92" id="ud3e38d92"><span data-lake-id="ud8e6e196" id="ud8e6e196">还是运行以下测试类：</span></p>
  <p data-lake-id="uf199414a" id="uf199414a"><br></p>
  <pre lang="java"><code>
package com.hollis;
import java.io.*;
/**
 * Created by hollis on 16/2/5.
 */
public class SerializableDemo1 {
    //为了便于理解，忽略关闭流操作及删除文件操作。真正编码时千万不要忘记
    //Exception直接抛出
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        //判断是否是同一个对象
        System.out.println(newInstance == Singleton.getSingleton());
    }
}
//true
</code></pre>
  <p data-lake-id="u71fb51fb" id="u71fb51fb"><br></p>
  <p data-lake-id="u1a737d7a" id="u1a737d7a"><span data-lake-id="u4409b888" id="u4409b888">本次输出结果为true。具体原理，我们回过头继续分析code 3中的第二段代码:</span></p>
  <p data-lake-id="ua988142c" id="ua988142c"><br></p>
  <pre lang="java"><code>
if (obj != null &amp;&amp;
            handles.lookupException(passHandle) == null &amp;&amp;
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj);
            if (unshared &amp;&amp; rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }
</code></pre>
  <p data-lake-id="ue3608415" id="ue3608415"><br></p>
  <p data-lake-id="ub9834b35" id="ub9834b35"><code data-lake-id="u25e97411" id="u25e97411"><span data-lake-id="uc1049afb" id="uc1049afb">hasReadResolveMethod</span></code><span data-lake-id="u5458ef55" id="u5458ef55">:如果实现了serializable 或者 externalizable接口的类中包含</span><code data-lake-id="u0ed97da8" id="u0ed97da8"><span data-lake-id="uf618de16" id="uf618de16">readResolve</span></code><span data-lake-id="u8891038a" id="u8891038a">则返回true</span></p>
  <p data-lake-id="ue7289cb2" id="ue7289cb2"><br></p>
  <p data-lake-id="ubddb9ae5" id="ubddb9ae5"><code data-lake-id="uc2963ff7" id="uc2963ff7"><span data-lake-id="u91272245" id="u91272245">invokeReadResolve</span></code><span data-lake-id="u23dd7d63" id="u23dd7d63">:通过反射的方式调用要被反序列化的类的readResolve方法。</span></p>
  <p data-lake-id="ufb230a78" id="ufb230a78"><br></p>
  <p data-lake-id="u6c4620b0" id="u6c4620b0"><span data-lake-id="ua76a3777" id="ua76a3777">所以，原理也就清楚了，只要在Singleton中定义readResolve方法，并在该方法中指定要返回的对象的生成策略，就可以防止单例被破坏。</span></p>
 </body>
</html>